package gbsip

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"gitee.com/tonyjia/gb28181/pkg/cache"
	"gitee.com/tonyjia/gb28181/pkg/config"
	model2 "gitee.com/tonyjia/gb28181/pkg/model"
	"gitee.com/tonyjia/gb28181/pkg/model/constant"
	"gitee.com/tonyjia/gb28181/pkg/util"
	"github.com/ghettovoice/gosip/sip"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
)

type (
	sipFactory struct{}
)

var (
	sipRequestFactory sipFactory
)

// createMessageRequest 创建Message类型请求
func (f sipFactory) createMessageRequest(d model2.Device, body string) sip.Request {
	requestBuilder := sip.NewRequestBuilder()
	requestBuilder.SetFrom(newFromAddress(newParams(map[string]string{"tag": util.RandString(32)})))

	to := newTo(d.DeviceID, d.Host, d.Port)
	requestBuilder.SetTo(to)
	requestBuilder.SetRecipient(to.Uri)
	requestBuilder.AddVia(newVia("TCP"))
	contentType := sip.ContentType(constant.ContentTypeXML)
	requestBuilder.SetContentType(&contentType)
	requestBuilder.SetMethod(sip.MESSAGE)
	requestBuilder.SetBody(body)

	requestBuilder.SetSeqNo(cast.ToUint(1))

	req, _ := requestBuilder.Build()
	return req
}

// createMessageResponse 创建Message类型请求
func (f sipFactory) createMessageResponse(user, host, port, body string) sip.Request {
	requestBuilder := sip.NewRequestBuilder()
	requestBuilder.SetFrom(newFromAddress(newParams(map[string]string{"tag": util.RandString(32)})))

	to := newTo(user, host, port)
	requestBuilder.SetTo(to)
	requestBuilder.SetRecipient(to.Uri)
	requestBuilder.AddVia(newVia("TCP"))
	contentType := sip.ContentType(constant.ContentTypeXML)
	requestBuilder.SetContentType(&contentType)
	requestBuilder.SetMethod(sip.MESSAGE)
	requestBuilder.SetBody(body)

	requestBuilder.SetSeqNo(cast.ToUint(1))

	req, _ := requestBuilder.Build()
	return req
}

// createInviteRequest 创建invite请求
func (f sipFactory) createInviteRequest(device model2.Device, mediaServer model2.MediaServer, channelId string, ssrc string) sip.Request {
	body := CreateSdpInfo(channelId, ssrc, mediaServer)

	requestBuilder := sip.NewRequestBuilder()
	to := newTo(channelId, device.Host, device.Port)
	requestBuilder.SetMethod(sip.INVITE)
	requestBuilder.SetFrom(newFromAddress(newParams(map[string]string{"tag": util.RandString(32)})))
	requestBuilder.SetTo(to)
	sipUri := &sip.SipUri{
		FUser: sip.String{Str: channelId},
		FHost: to.Uri.Host(),
	}
	requestBuilder.SetRecipient(sipUri)
	requestBuilder.AddVia(newVia("TCP"))
	requestBuilder.SetContact(newTo(config.SIPId(), config.SIPAddress(), config.SIPPort()))
	contentType := sip.ContentType(constant.ContentTypeSDP)
	requestBuilder.SetContentType(&contentType)
	requestBuilder.SetBody(body)
	ceq, err := cache.GetCache().GetCeq()
	if err != nil {
		log.Error("get ceq in cache fail,", err)
	} else {
		requestBuilder.SetSeqNo(cast.ToUint(ceq))
	}
	callID := sip.CallID(fmt.Sprintf("%s", util.RandString(32)))
	requestBuilder.SetCallID(&callID)
	header := sip.GenericHeader{
		HeaderName: "Subject",
		Contents:   fmt.Sprintf("%s:%s,%s:%d", channelId, ssrc, config.SIPId(), 0),
	}
	requestBuilder.AddHeader(&header)
	request, err := requestBuilder.Build()
	if err != nil {
		log.Error("发生错误：", err)
		return nil
	}

	return request
}

// 创建订阅请求
func (f sipFactory) createSubscribeRequest(device model2.Device, body, event string) (sip.Request, error) {
	builder := sip.NewRequestBuilder()
	to := newTo(device.DeviceID, device.Host, device.Port)
	// method
	builder.SetMethod(sip.SUBSCRIBE)
	// to
	builder.SetTo(to)
	// from
	builder.SetFrom(newFromAddress(newParams(map[string]string{"tag": util.RandString(32)})))

	sipUri := &sip.SipUri{
		FUser: sip.String{Str: device.DeviceID},
		FHost: to.Uri.Host(),
	}
	builder.SetRecipient(sipUri)
	// via
	builder.AddVia(newVia(device.TransPort))
	//contact
	builder.SetContact(newTo(config.SIPId(), config.SIPAddress(), config.SIPPort()))
	// content-type
	contentType := sip.ContentType(constant.ContentTypeXML)
	builder.SetContentType(&contentType)
	// body
	builder.SetBody(body)
	// ceq 顺序递增的请求
	//builder.SetSeqNo(cast.ToUint(rand.Uint32()))

	// callID 全局唯一的表示
	callID := sip.CallID(fmt.Sprintf("%s", util.RandString(32)))
	builder.SetCallID(&callID)

	// expires 设备的过期时间应该可设置
	expires := sip.Expires(3600)
	builder.SetExpires(&expires)

	eventHeader := &sip.GenericHeader{
		HeaderName: "Event",
		Contents:   event,
	}
	builder.AddHeader(eventHeader)
	request, err := builder.Build()
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return request, nil
}

// 创建通知请求
func (f sipFactory) createNotifyRequest(device model2.Device, body, event string) (sip.Request, error) {
	builder := sip.NewRequestBuilder()
	to := newTo(device.DeviceID, device.Host, device.Port)
	// method
	builder.SetMethod(sip.NOTIFY)
	// to
	builder.SetTo(to)
	// from
	builder.SetFrom(newFromAddress(newParams(map[string]string{"tag": util.RandString(32)})))

	sipUri := &sip.SipUri{
		FUser: sip.String{Str: device.DeviceID},
		FHost: to.Uri.Host(),
	}
	builder.SetRecipient(sipUri)
	// via
	builder.AddVia(newVia(device.TransPort))
	//contact
	builder.SetContact(newTo(config.SIPId(), config.SIPAddress(), config.SIPPort()))
	// content-type
	contentType := sip.ContentType(constant.ContentTypeXML)
	builder.SetContentType(&contentType)
	// body
	builder.SetBody(body)
	// ceq 顺序递增的请求
	//builder.SetSeqNo(cast.ToUint(rand.Uint32()))

	// callID 全局唯一的表示
	callID := sip.CallID(fmt.Sprintf("%s", util.RandString(32)))
	builder.SetCallID(&callID)

	// expires 设备的过期时间应该可设置
	expires := sip.Expires(3600)
	builder.SetExpires(&expires)

	eventHeader := &sip.GenericHeader{
		HeaderName: "Event",
		Contents:   event,
	}
	builder.AddHeader(eventHeader)
	request, err := builder.Build()
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return request, nil
}

// 创建bye请求
func (f sipFactory) createByeRequest(channelId string, device model2.Device, tx SipTX) (sip.Request, error) {

	fromAddress := newFromAddress(newParams(map[string]string{"tag": tx.FromTag}))

	toAddress := newTo(channelId, device.Host, device.Port)
	//toAddress.Params = newParams(map[string]string{"tag": tx.ToTag})

	via := newVia(device.TransPort)
	via.Params = newParams(map[string]string{"branch": tx.ViaBranch})

	callID := sip.CallID(tx.CallId)
	ceq, err := cache.GetCache().GetCeq()
	if err != nil {
		log.Error("get ceq in cache fail,", err)
		ceq = 0
	}

	request, err := sip.NewRequestBuilder().
		SetFrom(fromAddress).
		SetTo(toAddress).
		SetMethod(sip.BYE).
		AddVia(newVia("TCP")).
		SetContact(newTo(config.SIPId(), config.SIPAddress(), config.SIPPort())).
		SetCallID(&callID).
		SetSeqNo(cast.ToUint(ceq)).
		SetRecipient(&sip.SipUri{
			FUser: sip.String{Str: channelId},
			FHost: toAddress.Uri.Host(),
		}).Build()

	if err != nil {
		return nil, errors.WithMessage(err, "generate bye request fail")
	}
	return request, nil
}

// CreateRegisterRequest 创建注册请求
func (f sipFactory) CreateRegisterRequest(src, target model2.SipServerInfo, registerFlag int) (sip.Request, error) {
	var request sip.Request
	builder := sip.NewRequestBuilder()
	to := newTo(src.DeviceID, src.IP, strconv.Itoa(src.Port))
	// method
	builder.SetMethod(sip.REGISTER)
	// to register请求方地址
	builder.SetTo(to)
	// from 表明register请求时有服务器控控制的代理服务器发起
	builder.SetFrom(newFrom(src.DeviceID, src.IP, strconv.Itoa(src.Port)))
	port := sip.Port(target.Port)
	sipUri := &sip.SipUri{
		FUser: sip.String{Str: target.DeviceID},
		FHost: target.IP,
		FPort: &port,
	}
	builder.SetRecipient(sipUri)
	// via 目前请求所采用的路径
	builder.AddVia(newVia("TCP"))
	//contact 用户可以达的地方
	builder.SetContact(newTo(src.DeviceID, src.IP, strconv.Itoa(src.Port)))
	// content-type
	contentType := sip.ContentType(constant.ContentTypeXML)
	builder.SetContentType(&contentType)
	// callID
	callID := sip.CallID(fmt.Sprintf("%s", util.RandString(32)))
	builder.SetCallID(&callID)

	// expires
	expires := sip.Expires(registerFlag)
	builder.SetExpires(&expires)

	request, err := builder.Build()
	if err != nil {
		log.Error("发生错误：", err)
		return nil, err
	}
	return request, nil
}

// CreateRegisterRequestWithAuth  根据创建注册请求
func (f sipFactory) CreateRegisterRequestWithAuth(src, target model2.SipServerInfo, response sip.Response, registerFlag int) (sip.Request, error) {
	//区分是注销还是注册
	request, err := f.CreateRegisterRequest(src, target, registerFlag)
	if err != nil {
		return nil, err
	}
	//鉴权
	var authenticateHeaderName, authorizeHeaderName string
	if response.StatusCode() == 401 {
		// on 401 Unauthorized increase request seq num, add Authorization header and send once again
		authenticateHeaderName = "WWW-Authenticate"
		authorizeHeaderName = "Authorization"
	} else {
		// 407 Proxy authentication
		authenticateHeaderName = "Proxy-Authenticate"
		authorizeHeaderName = "Proxy-Authorization"
	}
	//进行鉴权
	if hdrs := response.GetHeaders(authenticateHeaderName); len(hdrs) > 0 {
		authenticateHeader := hdrs[0].(*sip.GenericHeader)
		auth := sip.AuthFromValue(authenticateHeader.Contents).
			SetMethod(string(request.Method())).
			SetUri(request.Recipient().String()).
			SetUsername(target.Username)
		auth.SetPassword(target.PassWord)
		if auth.Qop() == "auth" {
			auth.SetNc("00000001")
			encoder := md5.New()
			encoder.Write([]byte(target.Username + request.Recipient().String()))
			if target.PassWord != "" {
				encoder.Write([]byte(target.PassWord))
			}
			auth.SetCNonce(hex.EncodeToString(encoder.Sum(nil)))
		}
		auth.SetResponse(auth.CalcResponse())
		authenticateHeader.Contents = auth.String()
		if hdrs = request.GetHeaders(authorizeHeaderName); len(hdrs) > 0 {
			authorizationHeader := hdrs[0].Clone().(*sip.GenericHeader)
			authorizationHeader.Contents = auth.String()
			request.ReplaceHeaders(authorizationHeader.Name(), []sip.Header{authorizationHeader})
		} else {
			request.AppendHeader(&sip.GenericHeader{
				HeaderName: authorizeHeaderName,
				Contents:   auth.String(),
			})
		}
	} else {
		return nil, fmt.Errorf("authorize request: header '%s' not found in response", authenticateHeaderName)
	}
	return request, nil
}

// 从自身SIP服务获取地址返回FromHeader
func newFromAddress(params sip.Params) *sip.Address {
	return &sip.Address{
		Uri: &sip.SipUri{
			FUser: sip.String{Str: c.server.c.SipOption.Id},
			FHost: fmt.Sprintf("%s:%s", c.server.c.SipOption.Ip, c.server.c.SipOption.Port),
		},
		Params: params,
	}
}

func newTo(user, host, port string) *sip.Address {
	p := sip.Port(cast.ToUint16(port))
	if port == "0" {
		return &sip.Address{
			Uri: &sip.SipUri{
				FUser: sip.String{Str: user},
				FHost: host,
			},
		}
	}
	return &sip.Address{
		Uri: &sip.SipUri{
			FUser: sip.String{Str: user},
			FHost: host,
			FPort: &p,
		},
	}
}
func newFrom(user, host, port string) *sip.Address {
	p := sip.Port(cast.ToUint16(port))
	return &sip.Address{
		Uri: &sip.SipUri{
			FUser: sip.String{Str: user},
			FHost: host,
			FPort: &p,
		},
		Params: newParams(map[string]string{"tag": util.RandString(32)}),
	}
}

func newParams(m map[string]string) sip.Params {
	params := sip.NewParams()
	for k, v := range m {
		params.Add(k, sip.String{Str: v})
	}
	return params
}

func newVia(transport string) *sip.ViaHop {
	port, err := strconv.ParseInt(config.SIPPort(), 10, 64)
	if err != nil {
		log.Error("解析Via头部端口失败", err)
	}
	p := sip.Port(port)

	params := newParams(map[string]string{
		"branch": fmt.Sprintf("%s%d", "z9hG4bK", time.Now().UnixMilli()),
	})

	return &sip.ViaHop{
		ProtocolName:    "SIP",
		ProtocolVersion: "2.0",
		Transport:       transport,
		Host:            config.SIPAddress(),
		Port:            &p,
		Params:          params,
	}
}

func getResponse(tx sip.ClientTransaction) sip.Response {
	timer := time.NewTimer(1 * time.Second)

	for {
		select {
		case resp := <-tx.Responses():
			if resp.StatusCode() == sip.StatusCode(http.StatusContinue) ||
				resp.StatusCode() == sip.StatusCode(http.StatusSwitchingProtocols) {
				continue
			}
			return resp
		case <-timer.C:
			log.Error("获取响应超时")
			return nil
		}
	}
}
